//+------------------------------------------------------------------+
//|                                            SmartMartingaleCT.mq5 |
//|                                     Copyright 2024, Quant Academy |
//|                                           https://quantcode.cn   |
//+------------------------------------------------------------------+
#property copyright "Quant Academy"
#property version   "2.10"
#include <Trade\Trade.mqh>

//--- 输入参数
input double   InitialLots    = 0.01;     // 初始手数
input double   LotMultiplier  = 1.6;      // 加仓倍数
input int      MaxLayers      = 5;        // 最大层数
input int      GridStep       = 55;       // 网格间距(点)
input int      BaseTP         = 180;      // 基础止盈点数
input double   RiskPercent    = 1.2;      // 单笔风险百分比
input double   DailyLossLimit = 3.0;      // 单日最大亏损%
input double   EquityProtect  = 12.0;     // 净值保护%
input double   ProfitValue    = 0.0016;    // 盈利达%即触发
input bool     UseTrendFilter = true;     // 启用趋势过滤
input int      Magic          = 8888;     // 魔术码

bool triggered = false; // 防止重复触发标志

//--- 全局对象
CTrade trade;
double balance, equity, highestEquity;
int currentLayers = 0;
datetime lastTradeTime = 0;

//+------------------------------------------------------------------+
//| 专家初始化函数                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // 设置1秒定时器
   EventSetTimer(1);
   trade.SetExpertMagicNumber(Magic);
   balance = AccountInfoDouble(ACCOUNT_BALANCE);
   highestEquity = balance;
   return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer(); // 删除定时器
}

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
   datetime currentTime = TimeCurrent();                // 获取当前服务器时间
   datetime nextBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);   // 计算下一根K线开始时间
   int remainingSeconds = (int)(nextBarTime - currentTime); // 计算剩余秒数
   // 当剩余5秒且未触发过时执行
   if(remainingSeconds == -3 && !triggered)
   {
      triggered = true; // 设置触发标志
      OnTicks();
   }

   // 当剩余时间超过5秒时重置触发标志
   if(remainingSeconds > -5)
   {
      triggered = false;
   }
}

//+------------------------------------------------------------------+
//| 核心风控系统                                                     |
//+------------------------------------------------------------------+
bool RiskCheck()
{
   equity = AccountInfoDouble(ACCOUNT_EQUITY);
   balance = AccountInfoDouble(ACCOUNT_BALANCE);

   // 最高净值追踪
   if(equity > highestEquity) highestEquity = equity;

   // 多层风控检查
   if(CheckDailyLoss()) return false;            // 单日亏损限制
   if(CheckEquityProtect()) return false;        // 净值保护
   if(CheckPositionRisk()) return false;         // 持仓风险
   if(CheckMargin()) return false;               // 保证金检查
   
   return true;
}

//+------------------------------------------------------------------+
//| 单日亏损控制                                                     |
//+------------------------------------------------------------------+
bool CheckDailyLoss()
{
   MqlDateTime today;
   TimeCurrent(today);
   today.hour = today.min = today.sec = 0;
   double dayStart = AccountInfoDouble(ACCOUNT_BALANCE);
   
   if(equity < dayStart*(1-DailyLossLimit/100)) {
      CloseAllPositions();
      Print("触发单日最大亏损限制");
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 动态净值保护                                                     |
//+------------------------------------------------------------------+
bool CheckEquityProtect()
{
   double drawdown = (highestEquity - equity)/highestEquity*100;
  // Print("触发动态净值保护drawdown",drawdown,"==",EquityProtect);
   if(drawdown >= EquityProtect) {
      CloseAllPositions();
      Print("触发动态净值保护");
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 持仓风险控制                                                     |
//+------------------------------------------------------------------+
bool CheckPositionRisk()
{
   double positionRisk = CalculatePositionRisk();
   if(positionRisk > balance*RiskPercent/100) {
      Print("持仓风险超出限制: ", positionRisk);
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 保证金安全边际检查                                               |
//+------------------------------------------------------------------+
bool CheckMargin()
{
   double margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   if(margin < balance*0.1) {  // 保留10%保证金作为缓冲
      Print("可用保证金不足");
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 智能开仓逻辑                                                     |
//+------------------------------------------------------------------+
void ExecuteMartingale()
{  
   if(!RiskCheck()) return;
   if(!TrendFilterCheck() && UseTrendFilter) return;
   
   double lotSize = NormalizeLot(InitialLots * MathPow(LotMultiplier, currentLayers));
   double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double sl = 0, tp = price + BaseTP*_Point;
   if(currentLayers > 0) {
      double avgPrice = CalculateAveragePrice();
      if(price > avgPrice + GridStep*_Point) return; // 防止逆势加仓
   }
   
   
   for(int i= 0;i<=10;i++){
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double tp1 = price + 5*i*_Point;
      trade.Buy(lotSize, _Symbol, price, sl, tp1);
   }
   
   if(trade.Buy(lotSize, _Symbol, price, sl, tp)) {
      currentLayers++;
      lastTradeTime = TimeCurrent();
   }
}

//+------------------------------------------------------------------+
//| 趋势过滤器(示例: 双EMA交叉)                                      |
//+------------------------------------------------------------------+
bool TrendFilterCheck()
{
   double fastMA[], slowMA[];
   if(!UseTrendFilter) return true;
   double fastMA_handle = iMA(_Symbol, _Period, 50, 0, MODE_SMA, PRICE_CLOSE);
   double slowMA_handle = iMA(_Symbol, _Period, 200, 0, MODE_SMA, PRICE_CLOSE);
   
   CopyBuffer(fastMA_handle, 0, 0, 3, fastMA);
   CopyBuffer(slowMA_handle, 0, 0, 3, slowMA);
   bool maBullish = fastMA[0] > slowMA[0] && fastMA[1] <= slowMA[1];
  // Print("161",maBullish,"==",maBullish);
   return maBullish; // 仅允许顺势交易
}

//+------------------------------------------------------------------+
//| 平仓所有仓位                                                     |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   for(int i = PositionsTotal()-1; i >= 0; i--) {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC) == Magic) {
         double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         if(currentPrice/openPrice>1.5){
           trade.PositionClose(ticket);
         }
      }
   }
   currentLayers = 0;
}

//+------------------------------------------------------------------+
//| 智能盈利管理                                                     |
//+------------------------------------------------------------------+
void CheckProfitManagement()
{
   double totalProfit = 0;
   for(int i = PositionsTotal()-1; i >= 0; i--) {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC) == Magic) {
         totalProfit += PositionGetDouble(POSITION_PROFIT);
      }
   }

   // 动态止盈策略
   if(totalProfit > balance* ProfitValue) { // 盈利达0.5%即触发
      CloseAllPositions();
      Print("触发动态止盈","盈利达 ",ProfitValue);
   }
}

//+------------------------------------------------------------------+
//| 主交易逻辑                                                       |
//+------------------------------------------------------------------+
void OnTicks()
{
   CheckProfitManagement();
   if(PositionsTotal() == 0) {
      currentLayers = 0; // 重置层数
      ExecuteMartingale();
   }
   else {
      ExecuteMartingale(); // 继续加仓逻辑
   }
}

//+------------------------------------------------------------------+
//| 辅助函数：计算持仓风险                                           |
//+------------------------------------------------------------------+
double CalculatePositionRisk()
{
   double risk = 0;
   for(int i = 0; i < PositionsTotal(); i++) {
      if(PositionGetTicket(i) && PositionGetInteger(POSITION_MAGIC) == Magic) {
         risk += PositionGetDouble(POSITION_VOLUME) * 
                (PositionGetDouble(POSITION_PRICE_OPEN) - PositionGetDouble(POSITION_SL)) *
                 SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
      }
   }
   return risk;
}

//+------------------------------------------------------------------+
//| 辅助函数：计算平均进场价                                         |
//+------------------------------------------------------------------+
double CalculateAveragePrice()
{
   double totalVolume = 0;
   double priceSum = 0;
   
   for(int i = 0; i < PositionsTotal(); i++) {
      if(PositionGetTicket(i) && PositionGetInteger(POSITION_MAGIC) == Magic) {
         double volume = PositionGetDouble(POSITION_VOLUME);
         totalVolume += volume;
         priceSum += PositionGetDouble(POSITION_PRICE_OPEN) * volume;
      }
   }
   return (totalVolume > 0) ? priceSum / totalVolume : 0;
}

//+------------------------------------------------------------------+
//| 手数规范化函数                                                   |
//+------------------------------------------------------------------+
double NormalizeLot(double lots)
{
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   
   lots = MathMax(minLot, MathMin(lots, maxLot));
   return MathRound(lots/stepLot)*stepLot;
}